﻿Imports Co9320.NamaAlert.Engine
Imports Co9320.NamaAlert.Extension
Imports Co9320.NamaAlert.Model
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Windows.Threading
Imports Xceed.Wpf.DataGrid

Namespace ViewModel

    Public Class MainWindowViewModel
        Inherits ViewModelBase


        Private UserData As UserData
        Private WithEvents LiveCommentServer As LiveCommentServer

        Private _IsInitialized As Boolean
        Public ReadOnly Property IsInitialized() As Boolean
            Get
                Return _IsInitialized
            End Get
        End Property

        Private _DispatcherUIThread As Dispatcher
        Public ReadOnly Property DispatcherUIThread() As Dispatcher
            Get
                Return _DispatcherUIThread
            End Get
        End Property

        Public Sub New(ByVal path As ApplicationPath, ByVal dispatcher As Dispatcher)
            MyBase.New()
            Debug.Print("MainWindowViewModel New: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)
            _Path = path
            _DispatcherUIThread = dispatcher
        End Sub

        Public Sub InitializeAsync()
            _IsInitialized = True

            _LivePrograms = New ObservableCollection(Of LiveProgramViewModel)

            LiveCommentServer = LiveCommentServer.CreateAnonymousSession

            StationViewModel.CachePath = Path.CacheFolder

            'Initialize()
            Dim thread = New System.Threading.Thread(AddressOf Initialize)
            thread.Start()
        End Sub

        Private Sub Initialize()

            ' Live site phase
            If UISettings.DetailsOptions.EnableCommunityLevelAndMemberCountDownload Then
                LiveSite.DownloadArchiveAsync()
            End If

            ' Plugins phase
            InitializePlugins()

            ' User data phase
            UserData = New UserData(Path.DatabaseFileName)
            InitializeUserData()

            ' Notification phase

            SyncLock LiveProgramsSyncObject
                Dim ids = From p In LivePrograms Where p.Station.IsAlertTarget Select p.Station.Id
                For Each id In ids
                    LiveCommentServer.RegisterNotifiedStation(id)
                Next
            End SyncLock

            LiveNotification.Start()
            LiveCommentServer.Start()
        End Sub

        Private Sub InitializeUserData()
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(AddressOf InitializeUserData), Windows.Threading.DispatcherPriority.Background, Nothing)
                Exit Sub
            End If

            For Each programModel In UserData.LoadAlertTargetPrograms

                Dim programViewModel = New LiveProgramViewModel(programModel)

                ' プラグイン設定
                RefreshAlertManners(programViewModel.Station, programModel.Station.PluginGuids)
                'For Each guid In programModel.Station.PluginGuids
                '    programViewModel.Station.EnablePlugin(guid)
                'Next

                'Dim stationModel = programModel.Station
                'With programViewModel.Station
                '    .IsAlertTarget = stationModel.IsAlertTarget
                '    .Note = stationModel.Note
                '    .Rating = stationModel.Rating
                'End With

                programViewModel.Station.IsChanged = False

                Dim id = programModel.Station.Id

                'SyncLock LiveProgramsSyncObject
                '    Dim ps = From p In LivePrograms Where p.Station.Id = id
                '    If ps.Count > 0 Then
                '        ' 既に追加されている

                '        Dim isChanged = ps.SingleOrDefault.Station.IsChanged

                '        ' MEMO LiveNotification_LiveProgramStarted に同様の記述
                '        ps.SingleOrDefault.Station.IsAlertTarget = programModel.Station.IsAlertTarget
                '        ps.SingleOrDefault.Station.Note = programModel.Station.Note
                '        ps.SingleOrDefault.Station.Rating = programModel.Station.Rating
                '        ps.SingleOrDefault.Station.IsChanged = isChanged

                '        Continue For
                '    End If
                'End SyncLock

                ' 追加されていない場合
                AddLiveProgram(programViewModel)

            Next

            UserDataTimer.Interval = New TimeSpan(0, 1, 0)
            UserDataTimer.Start()
        End Sub

        Private WithEvents UserDataTimer As New DispatcherTimer


        Private Sub UserDataTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles UserDataTimer.Tick
            SaveUserData()
        End Sub

        Private Sub SaveUserData()
            SyncLock LiveProgramsSyncObject

                ' 通知対象
                Dim ps = From p In LivePrograms Where p.Station.IsChanged AndAlso p.Station.IsAlertTarget
                If ps.Count > 0 Then
                    Dim list = New List(Of LiveProgram)
                    For Each p In ps
                        list.Add(DirectCast(p.ToModel.Clone, LiveProgram))
                        p.Station.IsChanged = False
                    Next
                    UserData.SaveLivePrograms(list)
                End If

                ' 通知対象外
                Dim ss = From p In LivePrograms Where p.Station.IsChanged AndAlso Not p.Station.IsAlertTarget Select p.Station
                If ss.Count > 0 Then
                    Dim list = New List(Of Station)
                    For Each s In ss
                        list.Add(DirectCast(s.ToModel.Clone, Station))
                        s.IsChanged = False
                    Next
                    UserData.SaveStations(list)
                End If

            End SyncLock
        End Sub


#Region "Protected properties"

        Private _Path As ApplicationPath
        Protected ReadOnly Property Path() As ApplicationPath
            Get
                Return _Path
            End Get
        End Property

        Protected ReadOnly Property ConnectionString() As String
            Get
                Return String.Format(System.Globalization.CultureInfo.InvariantCulture, "Data Source={0};", Me.Path.DatabaseFileName)
            End Get
        End Property

#End Region

#Region "Plugins"
        ' TODO 削除されたプラグイン処理

        Private WithEvents PluginManager As New PluginFinder
        Private PluginsSyncObject As New Object

        Private Sub InitializePlugins()
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(AddressOf InitializePlugins), Windows.Threading.DispatcherPriority.Background, Nothing)
                Exit Sub
            End If

            Dim list = PluginManager.FindPlugins(Path.ApplicationFolder)

            For Each i In list
                Dim guid = i.Guid

                SyncLock PluginsSyncObject
                    Dim ps = From p In Plugins Where p.Guid = guid
                    If ps.Count > 0 Then
                        ' 設定があるプラグイン
                        ps.SingleOrDefault.SetEntity(New PluginWrapper(i.Path))
                        Continue For
                    End If
                End SyncLock

                ' 新しいプラグイン
                Dim viewModel = New PluginViewModel(New PluginWrapper(i.Path))

                If viewModel.Guid.ToString.ToUpperInvariant = "F1E417E1-EF49-4e5d-8783-F9A882AF7FBD".ToUpperInvariant Then
                    viewModel.IsEnabled = True
                End If

                AddPlugin(viewModel)
            Next

        End Sub

        'Private Sub PluginManager_PluginsFound(ByVal sender As Object, ByVal e As Engine.PluginsFoundEventArgs) Handles PluginManager.PluginsFound
        '    If Not DispatcherUIThread.CheckAccess Then
        '        DispatcherUIThread.Invoke(New Action(Of Object, Engine.PluginsFoundEventArgs)(AddressOf PluginManager_PluginsFound), Windows.Threading.DispatcherPriority.Background, New Object() {sender, e})
        '        Exit Sub
        '    End If
        '    Debug.Print("PluginManager_PluginsFound: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)

        '    For Each i In e.Info
        '        Dim guid = i.Guid

        '        SyncLock PluginsSyncObject
        '            Dim ps = From p In Plugins Where p.Guid = guid
        '            If ps.Count > 0 Then
        '                ' 設定があるプラグイン
        '                ps.SingleOrDefault.SetEntity(New PluginWrapper(i.Path))
        '                Continue For
        '            End If
        '        End SyncLock

        '        ' 新しいプラグイン
        '        Dim viewModel = New PluginViewModel(New PluginWrapper(i.Path))
        '        AddPlugin(viewModel)
        '    Next

        '    'RefreshAlertManners()




        'End Sub

        'Private Sub RefreshAlertManners()
        '    If Not DispatcherUIThread.CheckAccess Then
        '        DispatcherUIThread.Invoke(New Action(AddressOf RefreshAlertManners), Windows.Threading.DispatcherPriority.Background, Nothing)
        '        Exit Sub
        '    End If
        '    Debug.Print("RefreshAlertManners: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)

        '    SyncLock LiveProgramsSyncObject
        '        For Each p In LivePrograms
        '            p.Station.RefreshAlertManners(Me.Plugins)
        '        Next
        '    End SyncLock
        'End Sub

        Private Sub AddPlugin(ByVal plugin As PluginViewModel)
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(Of PluginViewModel)(AddressOf AddPlugin), Windows.Threading.DispatcherPriority.Background, New Object() {plugin})
                Exit Sub
            End If

            SyncLock PluginsSyncObject
                Plugins.Add(plugin)
            End SyncLock
        End Sub

        Private WithEvents _Plugins As  ObservableCollection(Of PluginViewModel)
        Public ReadOnly Property Plugins() As ObservableCollection(Of PluginViewModel)
            Get
                SyncLock PluginsSyncObject
                    If _Plugins Is Nothing Then
                        _Plugins = New ObservableCollection(Of PluginViewModel)
                    End If
                    Return _Plugins
                End SyncLock
            End Get
        End Property

        Private WithEvents _PluginsViewSource As DataGridCollectionViewSource
        Public ReadOnly Property PluginsViewSource() As DataGridCollectionViewSource
            Get
                If _PluginsViewSource Is Nothing Then
                    _PluginsViewSource = New DataGridCollectionViewSource
                    _PluginsViewSource.Source = Plugins
                End If
                Return _PluginsViewSource
            End Get
        End Property

        Private Sub _Plugins_CollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) Handles _Plugins.CollectionChanged
            If e.Action = Specialized.NotifyCollectionChangedAction.Add Then
                For Each i In e.NewItems
                    Dim plugin = DirectCast(i, PluginViewModel)
                    AddHandler plugin.RequestEditPluginOptions, AddressOf OnEditPluginOptions
                    AddHandler plugin.RequestShowBalloonTip, AddressOf OnRequestShowBalloonTip
                Next
            ElseIf e.Action = Specialized.NotifyCollectionChangedAction.Remove Then
                For Each i In e.OldItems
                    RemoveHandler DirectCast(i, PluginViewModel).RequestEditPluginOptions, AddressOf OnEditPluginOptions
                Next
            End If
        End Sub

        Public Event RequestEditPluginOptions As EventHandler(Of EventArgs)
        Private Sub OnEditPluginOptions(ByVal sender As Object, ByVal e As EventArgs)
            RaiseEvent RequestEditPluginOptions(sender, e)
        End Sub

        Public Event RequestShowBalloonTip As EventHandler(Of ShowBalloonTipEventArgs)
        Protected Overridable Sub OnRequestShowBalloonTip(ByVal sender As Object, ByVal e As ShowBalloonTipEventArgs)
            RaiseEvent RequestShowBalloonTip(sender, e)
        End Sub

        'Private Sub ShowBalloonTip(ByVal title As String, ByVal text As String)
        '    OnRequestShowBalloonTip(Me, New ShowBalloonTipEventArgs(title, text))
        'End Sub

#End Region

#Region "Live Programs"
        Private LiveProgramsSyncObject As New Object
        Private WithEvents LiveNotification As New LiveNotification(New TimeSpan(0, 0, 5))

        Private WithEvents _LivePrograms As ObservableCollection(Of LiveProgramViewModel)
        Public ReadOnly Property LivePrograms() As ObservableCollection(Of LiveProgramViewModel)
            Get
                Return _LivePrograms
            End Get
        End Property

        Private WithEvents _LiveProgramsViewSource As DataGridCollectionViewSource
        Public ReadOnly Property LiveProgramsViewSource() As DataGridCollectionViewSource
            Get
                If _LiveProgramsViewSource Is Nothing Then
                    _LiveProgramsViewSource = New DataGridCollectionViewSource
                    _LiveProgramsViewSource.SortDescriptions.Add(New SortDescription("Group", ListSortDirection.Ascending)) ' MEMO DataGrid グループ のソート順設定に必要な記述
                    _LiveProgramsViewSource.Source = _LivePrograms

                End If
                Return _LiveProgramsViewSource
            End Get
        End Property

        Public ReadOnly Property AlertedProgramsCount() As Integer
            Get
                SyncLock LiveProgramsSyncObject
                    Return (From p In LivePrograms Where p.IsAlerted).Count
                End SyncLock
            End Get
        End Property

        Private Sub AddLiveProgram(ByVal program As LiveProgramViewModel)
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(Of LiveProgramViewModel)(AddressOf AddLiveProgram), Windows.Threading.DispatcherPriority.Background, New Object() {program})
                Exit Sub
            End If

            SyncLock LiveProgramsSyncObject
                LivePrograms.Add(program)
            End SyncLock
        End Sub

        Private Sub RemoveLiveProgram(ByVal program As LiveProgramViewModel)
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(Of LiveProgramViewModel)(AddressOf RemoveLiveProgram), Windows.Threading.DispatcherPriority.Background, New Object() {program})
                Exit Sub
            End If

            SyncLock LiveProgramsSyncObject
                LivePrograms.Remove(program)
            End SyncLock
        End Sub

        Private Sub RefreshAlertManners(ByVal station As StationViewModel, ByVal guids As IList(Of Guid))
            If Not DispatcherUIThread.CheckAccess Then
                DispatcherUIThread.Invoke(New Action(Of StationViewModel, IList(Of Guid))(AddressOf RefreshAlertManners), Windows.Threading.DispatcherPriority.Background, New Object() {station, guids})
                Exit Sub
            End If

            station.RefreshAlertManners(Me.Plugins, guids)
        End Sub


        Private Sub LiveCommentServer_ProgramStart(ByVal sender As Object, ByVal e As Model.LiveProgramEventArgs) Handles LiveCommentServer.LiveProgramStarted

        End Sub

        Private Sub LiveNotification_LiveProgramStarted(ByVal sender As Object, ByVal e As Model.LiveProgramEventArgs) Handles LiveNotification.LiveProgramStarted
            'If _Dispatcher.Thread.ManagedThreadId <> System.Threading.Thread.CurrentThread.ManagedThreadId Then
            '    MyDispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, _
            '                      New Action(Of Object, Model.LiveProgramEventArgs)(AddressOf LiveNotification_LiveProgramStarted), sender, New Object() {e})
            '    Exit Sub
            'End If

            'Debug.Print("LiveNotification_LiveProgramStarted: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)
            'Debug.Assert(e.LiveProgram.Station.Id.Length > 2)
            'If e.LiveProgram.Station.Id.Length < 3 Then
            '    Exit Sub
            'End If

            Dim program As LiveProgramViewModel

            'SyncLock LiveProgramsSyncObject
            '' 同じIDの放送がある場合更新
            'Dim ps1 = From p In LivePrograms Where p.Id = e.LiveProgram.Id
            'If ps1.Count > 0 Then
            '    UpdateLiveProgram(e.LiveProgram)

            '    ' TODO 予約放送開始の場合通知
            'End If

            ' NG設定
            If UISettings.NGOptions.IsEnabled Then
                Dim ss = From s In UISettings.NGOptions.Stations Where s.Id = e.LiveProgram.Station.Id
                If ss.Count > 0 Then
                    Exit Sub
                End If
            End If

            ' ViewModel 生成
            program = New LiveProgramViewModel(e.LiveProgram) With {.IsOnAir = True}
            'End SyncLock

            



            ' 放送局情報取得
            Dim stationEntity = UserData.LoadStation(program.Station.Id)
            If stationEntity IsNot Nothing Then
                With program.Station
                    .IsAlertTarget = stationEntity.IsAlertTarget
                    .Note = stationEntity.Note
                    .Rating = stationEntity.Rating
                End With

                ' プラグイン設定
                RefreshAlertManners(program.Station, stationEntity.PluginGuids)
            Else
                ' プラグイン設定
                RefreshAlertManners(program.Station, New List(Of Guid))
            End If
            program.Station.IsChanged = False

            ' Community Level, MemberCount
            If program.Station.Kind = StationKind.Community AndAlso LiveSite.IsCommunitiesTableEnabled Then
                Dim station = LiveSite.GetCommunityLevelAndMemberCount(program.Station.Id)
                If station IsNot Nothing Then

                    program.Station.Level = station.Level
                    program.Station.MemberCount = station.MemberCount

                End If
            End If

            ' 同じIDの放送局がある場合 古い方を削除（オフエア or 終了済み or 予約直前通知）
            Dim deleteTarget As LiveProgramViewModel = Nothing
            SyncLock LiveProgramsSyncObject
                Dim ps2 = From p In LivePrograms Where p.Station.Id = program.Station.Id
                If ps2.Count > 0 Then
                    deleteTarget = ps2.SingleOrDefault
                End If
            End SyncLock

            If deleteTarget IsNot Nothing Then
                RemoveLiveProgram(deleteTarget)
            End If

            ' 追加
            AddLiveProgram(program)
            
            ' プラグイン
            'SyncLock PluginsSyncObject
            'For Each p In Plugins
            '    Select Case p.RequireLiveProgramNotify
            '        Case LiveProgramNotify.All
            '            p.ExecuteWhenLiveProgramStarted(DirectCast(program.ToModel.Clone, Model.LiveProgram))

            '        Case LiveProgramNotify.FavoriteOnly
            '            If program.Station.IsAlertTarget Then
            '                p.ExecuteWhenLiveProgramStarted(DirectCast(program.ToModel.Clone, Model.LiveProgram))
            '            End If
            '    End Select
            'Next
            'End SyncLock

            ' アラート
            If program.Station.IsAlertTarget AndAlso Not program.IsAlerted Then

                ' TODO サウンドアラート

                ' アラート
                Dim plugs = From plugin In Plugins _
                            From am In program.Station.AlertManners _
                            Where plugin.Guid = am.Plugin.Guid _
                            AndAlso plugin.HasAlertFunction _
                            AndAlso plugin.IsEnabled _
                            AndAlso am.IsEnabled _
                            Select plugin

                For Each p In plugs
                    p.ExecuteAlert(DirectCast(program.ToModel.Clone, Model.LiveProgram))
                Next
            End If


            'UISettings.DataGrid.RefreshGroup()
        End Sub



        Private Sub LiveNotification_LiveProgramClosed(ByVal sender As Object, ByVal e As Model.LiveProgramEventArgs) Handles LiveNotification.LiveProgramClosed

            Dim program As LiveProgramViewModel
            SyncLock LiveProgramsSyncObject
                Dim ps = From p In LivePrograms Where p.Id = e.LiveProgram.Id
                If ps.Count = 0 Then
                    Exit Sub
                End If
                program = ps.SingleOrDefault
            End SyncLock

            If program.Station.IsAlertTarget Then
                program.IsOnAir = False
                UserData.SaveLiveProgram(DirectCast(program.ToModel.Clone, LiveProgram))
            Else
                RemoveLiveProgram(program)
            End If


        End Sub

        Private Sub LiveNotification_LiveProgramUpdated(ByVal sender As Object, ByVal e As Model.LiveProgramEventArgs) Handles LiveNotification.LiveProgramUpdated
            SyncLock LiveProgramsSyncObject
                UpdateLiveProgram(e.LiveProgram)
            End SyncLock
        End Sub

        ''' <summary>
        ''' LiveProgramsの情報を更新
        ''' </summary>
        ''' <param name="program">更新情報を持っている生放送</param>
        ''' <remarks>SyncLock内から呼び出されるメソッド</remarks>
        Private Sub UpdateLiveProgram(ByVal program As LiveProgram)
            ' MEMO no SyncLock

            Dim ps = From p In LivePrograms Where p.Id = program.Id
            If ps.Count = 0 Then
                Exit Sub
            End If

            With ps.FirstOrDefault
                If program.CommentCount > .CommentCount Then
                    .CommentCount = program.CommentCount
                End If

                If program.VisitorCount > .VisitorCount Then
                    .VisitorCount = program.VisitorCount
                End If

                If Not .IsReserved AndAlso program.IsReserved Then
                    .IsReserved = True
                End If

                If program.CasterName <> "" Then
                    .Caster = program.CasterName
                End If

                If program.Content <> "" Then
                    .Content = program.Content
                End If

                'If program.Station.IsAlertTarget Then
                '    .Station.IsAlertTarget = True
                'End If
            End With

        End Sub

#End Region

#Region "System Messages"

        Private WithEvents _SystemMessages As New ObservableCollection(Of SystemMessage)
        Public ReadOnly Property SystemMessages() As ObservableCollection(Of SystemMessage)
            Get
                Return _SystemMessages
            End Get
        End Property

        Private _UISettings As New UISettingsViewModel
        Public ReadOnly Property UISettings() As UISettingsViewModel
            Get
                Return _UISettings
            End Get
        End Property

#End Region

        Private WithEvents LiveSite As New LiveSite("")

        Private Sub LiveSite_DownloadCompleted(ByVal sender As Object, ByVal e As System.EventArgs) Handles LiveSite.DownloadCompleted

            SyncLock LiveProgramsSyncObject

                For Each p In LivePrograms
                    If p.Station.Kind <> StationKind.Community Then
                        Exit For
                    End If

                    Dim station = LiveSite.GetCommunityLevelAndMemberCount(p.Station.Id)
                    If station IsNot Nothing Then
                        p.Station.Level = station.Level
                        p.Station.MemberCount = station.MemberCount
                    End If
                Next

            End SyncLock

        End Sub



#Region "Add and delete alert target"

        Private _AddAlertTargetCommand As ICommand
        Public ReadOnly Property AddAlertTargetCommand() As ICommand
            Get
                If _AddAlertTargetCommand Is Nothing Then
                    _AddAlertTargetCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.AddAlertTarget), New Predicate(Of Object)(AddressOf CanOperateAlertTarget))
                End If
                Return _AddAlertTargetCommand
            End Get
        End Property

        Private Sub AddAlertTarget(ByVal obj As Object)
            DirectCast(obj, StationViewModel).IsAlertTarget = True
            Filter()
        End Sub

        Private _DeleteAlertTargetCommand As ICommand
        Public ReadOnly Property DeleteAlertTargetCommand() As ICommand
            Get
                If _DeleteAlertTargetCommand Is Nothing Then
                    _DeleteAlertTargetCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.DeleteAlertTarget), New Predicate(Of Object)(AddressOf CanOperateAlertTarget))
                End If
                Return _DeleteAlertTargetCommand
            End Get
        End Property

        Private Sub DeleteAlertTarget(ByVal obj As Object)
            Dim station = DirectCast(obj, StationViewModel)
            station.IsAlertTarget = False
            station.AlertManners.Clear()

            ' TODO オフエアなら削除
            Filter()
        End Sub

        Private Function CanOperateAlertTarget(ByVal obj As Object) As Boolean
            Return TypeOf obj Is StationViewModel
        End Function

#End Region

#Region "Search"

        Private _SearchCommand As ICommand
        Public ReadOnly Property SearchCommand() As ICommand
            Get
                If _SearchCommand Is Nothing Then
                    Dim action As New Action(Of Object)(AddressOf Me.TriggerSearch)
                    _SearchCommand = New RelayCommand(action)
                End If
                Return _SearchCommand
            End Get
        End Property

        Private _ClearSearchCommand As ICommand
        Public ReadOnly Property ClearSearchCommand() As ICommand
            Get
                If _ClearSearchCommand Is Nothing Then
                    Dim action As New Action(Of Object)(AddressOf Me.ClearSearch)
                    _ClearSearchCommand = New RelayCommand(action)
                End If
                Return _ClearSearchCommand
            End Get
        End Property

        Private _SearchText As String = ""
        Public Property SearchText() As String
            Get
                Return _SearchText
            End Get
            Set(ByVal value As String)
                _SearchText = value
                OnPropertyChanged("SearchText")
            End Set
        End Property

        Private InputedSearchText As String
        Private NormalizedSearchText As String

        Private Sub TriggerSearch(ByVal obj As Object)
            InputedSearchText = SearchText.Trim
            NormalizedSearchText = SearchText.ToNormalize(UISettings.SearchOptions) & " " ' MEMO 末尾スペースは検索処理用
            Filter()
        End Sub

        Private Sub ClearSearch(ByVal obj As Object)
            Me.SearchText = ""
        End Sub

        Private Function Search(ByVal program As LiveProgramViewModel) As Boolean

            If UISettings.SearchOptions.UseRegEx Then
                Return SearchUsingRegEx(program, InputedSearchText)
            End If

            Dim andWords = New List(Of String)
            Dim orWords = New List(Of String)
            Dim notWords = New List(Of String)
            Dim targetList As IList(Of String) = andWords
            Dim word As String = ""

            ' トークン分解 ループ
            For Each c In NormalizedSearchText
                If " 　&|-".Contains(c) AndAlso word.Length > 0 Then
                    targetList.Add(word)
                    word = ""
                End If

                Select Case c
                    Case " "c, "　"c, "&"c
                        targetList = andWords
                    Case "|"c
                        targetList = orWords
                    Case "-"c
                        targetList = notWords
                    Case Else
                        word &= c
                End Select
            Next

            ' 絞り込みワードが0個ならtrue返して関数終了
            If andWords.Count = 0 AndAlso orWords.Count = 0 AndAlso notWords.Count = 0 Then
                Return True
            End If

            ' 検索対象文字列
            Dim target = String.Join(" ", New String() {program.Title, program.Station.Name, program.Content, If(program.Caster, "")}).ToNormalize(UISettings.SearchOptions)

            ' NOTキーワードが含まれているか判別
            For Each w In notWords
                If target.Contains(w) Then
                    Return False
                End If
            Next

            ' ORキーワードが含まれているか判別
            For Each w In orWords
                If target.Contains(w) Then
                    Return True
                End If
            Next

            ' ANDキーワード全て含まれているか判別
            If andWords.Count = 0 Then
                Return False
            End If

            For Each w In andWords
                If Not target.Contains(w) Then
                    Return False
                End If
            Next

            Return True

        End Function

        Private Function SearchUsingRegEx(ByVal program As LiveProgramViewModel, ByVal pattern As String) As Boolean
            If pattern = "" Then
                Return False
            End If

            Dim target = String.Join(vbCrLf, New String() {program.Title, program.Station.Name, program.Content, If(program.Caster, "")}).ToNormalize(UISettings.SearchOptions)
            Try
                Return System.Text.RegularExpressions.Regex.IsMatch(target, pattern.ToNormalize(UISettings.SearchOptions), Text.RegularExpressions.RegexOptions.Multiline)
            Catch ex As ArgumentException
                Return False
            End Try

        End Function

#End Region

#Region "Filter"

        Private _FilterCommand As ICommand
        Public ReadOnly Property FilterCommand() As ICommand
            Get
                If _FilterCommand Is Nothing Then
                    _FilterCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.Filter))
                End If
                Return _FilterCommand
            End Get
        End Property

        Private Sub Filter()
            Filter(Nothing)
        End Sub

        Private Sub Filter(ByVal obj As Object)
            If Me.LiveProgramsViewSource IsNot Nothing Then
                Me.LiveProgramsViewSource.View.Refresh()
            End If
        End Sub

        Private Sub _LiveProgramsViewSource_Filter(ByVal sender As Object, ByVal e As System.Windows.Data.FilterEventArgs) Handles _LiveProgramsViewSource.Filter

            If Not TypeOf e.Item Is LiveProgramViewModel Then
                e.Accepted = False
                Exit Sub
            End If

            Dim program = DirectCast(e.Item, LiveProgramViewModel)
            If program Is Nothing OrElse program Is DBNull.Value Then
                e.Accepted = False
                Exit Sub
            End If

            ' 評価フィルター
            If UISettings.DataGrid.FilterByStar5 AndAlso program.Station.Rating = 5 Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByStar4 AndAlso program.Station.Rating = 4 Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByStar3 AndAlso program.Station.Rating = 3 Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByStar2 AndAlso program.Station.Rating = 2 Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByStar1 AndAlso program.Station.Rating = 1 Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByStar0 AndAlso program.Station.Rating = 0 Then
                e.Accepted = False
                Exit Sub
            End If

            ' 生放送種別 フィルター
            If UISettings.DataGrid.FilterByChannel AndAlso program.Station.Kind = StationKind.Channel Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByCommunity AndAlso program.Station.Kind = StationKind.Community Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByMemberOnly AndAlso program.IsMemberOnly Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByFace AndAlso program.IsFace Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByReserved AndAlso program.IsReserved Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByAlertTarget AndAlso program.Station.IsAlertTarget Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByOnAir AndAlso program.IsOnAir Then
                e.Accepted = False
                Exit Sub
            End If

            If UISettings.DataGrid.FilterByOffAir AndAlso Not program.IsOnAir Then
                e.Accepted = False
                Exit Sub
            End If

            ' 検索フィルター
            If InputedSearchText = "" Then
                e.Accepted = True
                Exit Sub
            End If

            Dim watch = New Diagnostics.Stopwatch
            watch.Start()

            e.Accepted = Search(program)

            Debug.WriteLine(watch.ElapsedMilliseconds)
        End Sub

#End Region

#Region "Copy and open URI Commands"

        Private _OpenUriCommand As ICommand
        Public ReadOnly Property OpenUriCommand() As ICommand
            Get
                If _OpenUriCommand Is Nothing Then
                    _OpenUriCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.OpenUri), New Predicate(Of Object)(AddressOf HasText))
                End If
                Return _OpenUriCommand
            End Get
        End Property

        Private Sub OpenUri(ByVal obj As Object)
            If obj Is Nothing Then
                Exit Sub
            End If
            Dim uri = If(TypeOf obj Is Uri, DirectCast(obj, Uri), New Uri(CStr(obj)))
            Manager.BrowserManager.OpenUri(uri, UISettings.BrowserOptions)
        End Sub

        Private _CopyUriCommand As ICommand
        Public ReadOnly Property CopyUriCommand() As ICommand
            Get
                If _CopyUriCommand Is Nothing Then
                    _CopyUriCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.CopyUri), New Predicate(Of Object)(AddressOf HasText))
                End If
                Return _CopyUriCommand
            End Get
        End Property

        Private Sub CopyUri(ByVal obj As Object)
            Dim uri = DirectCast(obj, Uri)
            Try
                Clipboard.SetText(uri.ToString)
            Catch ex As Exception

            End Try
        End Sub

        Private Function HasText(ByVal obj As Object) As Boolean
            Return obj IsNot Nothing AndAlso (TypeOf obj Is Uri OrElse TypeOf obj Is String)
        End Function

#End Region

#Region "Application Shutdown"
        Public Event ApplicationShutdown As EventHandler(Of EventArgs)
        Protected Overridable Sub OnApplicationShutdown(ByVal e As EventArgs)
            RaiseEvent ApplicationShutdown(Me, e)
        End Sub

        Private _ShutdownApplicationCommand As ICommand
        Public ReadOnly Property ShutdownApplicationCommand() As ICommand
            Get
                If _ShutdownApplicationCommand Is Nothing Then
                    Dim action = New Action(Of Object)(AddressOf Me.ShutdownApplication)
                    _ShutdownApplicationCommand = New RelayCommand(action)
                End If
                Return _ShutdownApplicationCommand
            End Get
        End Property

        Private Sub ShutdownApplication(ByVal obj As Object)

            LiveNotification.Dispose()
            SaveUserData()

            'db.Dispose()
            OnApplicationShutdown(New EventArgs)
        End Sub
#End Region

#Region "Application Show"
        Public Event ApplicationShow As EventHandler(Of EventArgs)
        Protected Overridable Sub OnApplicationShow(ByVal e As EventArgs)
            RaiseEvent ApplicationShow(Me, e)
        End Sub

        Private _ShowApplicationCommand As ICommand
        Public ReadOnly Property ShowApplicationCommand() As ICommand
            Get
                If _ShowApplicationCommand Is Nothing Then
                    _ShowApplicationCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.ShowApplication))
                End If
                Return _ShowApplicationCommand
            End Get
        End Property

        Private Sub ShowApplication(ByVal obj As Object)
            OnApplicationShow(New EventArgs)
        End Sub
#End Region

#Region "Show Topmost"
        Public Event RequestShowTopmost As EventHandler(Of BooleanEventArgs)
        Protected Overridable Sub OnShowTopmost(ByVal e As BooleanEventArgs)
            RaiseEvent RequestShowTopmost(Me, e)
        End Sub

        Private _ShowTopmostCommand As ICommand
        Public ReadOnly Property ShowTopmostCommand() As ICommand
            Get
                If _ShowTopmostCommand Is Nothing Then
                    _ShowTopmostCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.ShowTopmost))
                End If
                Return _ShowTopmostCommand
            End Get
        End Property

        Private Sub ShowTopmost(ByVal obj As Object)
            OnShowTopmost(New BooleanEventArgs(UISettings.Topmost))
        End Sub
#End Region

#Region "Show option window"

        Public Event RequestShowOptionsWindow As EventHandler(Of EventArgs)
        Protected Overridable Sub OnShowOptionsWindow(ByVal e As EventArgs)
            RaiseEvent RequestShowOptionsWindow(Me, e)
        End Sub

        Private _ShowOptionsWindowCommand As ICommand
        Public ReadOnly Property ShowOptionsWindowCommand() As ICommand
            Get
                If _ShowOptionsWindowCommand Is Nothing Then
                    _ShowOptionsWindowCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.ShowOptionsWindow))
                End If
                Return _ShowOptionsWindowCommand
            End Get
        End Property

        Private Sub ShowOptionsWindow(ByVal obj As Object)
            OnShowOptionsWindow(New EventArgs)
        End Sub

#End Region

#Region "Show import window"

        Public Event RequestShowImportWindow As EventHandler(Of EventArgs)
        Protected Overridable Sub OnShowImportWindow(ByVal e As EventArgs)
            RaiseEvent RequestShowImportWindow(Me, e)
        End Sub

        Private _ShowImportWindowCommand As ICommand
        Public ReadOnly Property ShowImportWindowCommand() As ICommand
            Get
                If _ShowImportWindowCommand Is Nothing Then
                    _ShowImportWindowCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.ShowImportWindow))
                End If
                Return _ShowImportWindowCommand
            End Get
        End Property

        Private Sub ShowImportWindow(ByVal obj As Object)
            OnShowImportWindow(New EventArgs)
        End Sub

#End Region

    End Class


End Namespace
